﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using ScrumTable.Common.Collections;
using ScrumTable.DL.Data.Generic.Cache;

#endregion

namespace ScrumTable.DL.Data.Generic.Test.Cache
{
    /// <summary>
    /// This is the test class for CacheProject class.
    /// </summary>
    [TestClass]
    public class CacheProjectTest : CacheElementBaseTest<CacheProject>
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        #endregion

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new CacheProjectTest instance.
        /// </summary>
        public CacheProjectTest()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Use TestInitialize to run code before running each test .
        /// </summary>
        [TestInitialize]
        public override void TestInitialize()
        {
            base.TestInitialize();
            ToTest = CacheContext.Projects.TryGetById(Resources.Project1.Id);
        }

        /// <summary>
        /// Use TestCleanup to run code after each test has run.
        /// </summary>
        [TestCleanup]
        public override void TestCleanup()
        {
            ToTest = null;
            base.TestCleanup();
        }

        [TestMethod]
        public void TestProject()
        {
            Assert.AreSame(ToTest.Project, ToTest);
        }

        [TestMethod]
        public void TestLastSaveTime()
        {
            DateTime newSaveTime = DateTime.Now;
            DateTime newSaveTimeBefore = DateTime.Now.Subtract(TimeSpan.FromSeconds(1.0));
            DateTime lastSaveTime = ToTest.LastSaveTime;
            Assert.AreNotEqual(newSaveTime, lastSaveTime);

            // export and import changes and change last save time
            CacheDataChanges changes = DataContext.ExportChanges();
            changes.LastSaveTime = newSaveTime;
            DataContext.ImportChanges(changes);

            // make sure last save time is imported
            Assert.AreEqual(newSaveTime, ToTest.LastSaveTime);

            // export and import changes and change last save time to a lower value
            CacheDataChanges changesWithLowerSaveTime = DataContext.ExportChanges();
            changes.LastSaveTime = newSaveTimeBefore;
            DataContext.ImportChanges(changesWithLowerSaveTime);

            // make sure last save time is NOT imported
            Assert.AreEqual(newSaveTime, ToTest.LastSaveTime);
        }

        [TestMethod]
        public void TestRegistrationUris()
        {
            Assert.AreEqual(ResourceManager.Project1RsRegistration, ToTest.ReportingPortal);
            Assert.AreEqual(ResourceManager.Project1WssRegistration, ToTest.ProjectPortal);
        }

        [TestMethod]
        public void TestTypes()
        {
            Assert.AreEqual(Resources.FieldDefinitionsProj1.TaskType.Id, ToTest.TaskType.Id);
            Assert.IsTrue(ToTest.TaskType.IsContextConnected);
            Assert.AreEqual(Resources.FieldDefinitionsProj1.UserStoryType.Id, ToTest.UserStoryType.Id);
            Assert.IsTrue(ToTest.UserStoryType.IsContextConnected);
            Assert.AreEqual(Resources.FieldDefinitionsProj1.BugType.Id, ToTest.BugType.Id);
            Assert.IsTrue(ToTest.BugType.IsContextConnected);
        }

        [TestMethod]
        public void TestImportData()
        {
            Assert.AreEqual(
                ResourceManager.Project1IterationCount,
                ToTest.Iterations.Count);
            Assert.AreEqual(
                ResourceManager.Project1MemberCount,
                ToTest.Members.Count);
            Assert.AreEqual(
                ResourceManager.Project1Iteration1UserStoryCount,
                ToTest.WorkItems.GetChildren(Resources.GetIterationChain(ToTest, ResourceManager.Iteration1Id), ToTest.UserStoryType).Count);
            Assert.AreEqual(
                ResourceManager.Project1Iteration1UserStory3TaskCount,
                ToTest.WorkItems.GetChildren(Resources.GetUserStoryChain(ToTest, ResourceManager.Iteration1Id, ResourceManager.Iteration3Id), ToTest.TaskType).Count);
            Assert.AreEqual(
                ResourceManager.Project1UserStory1TaskCount,
                ToTest.WorkItems.GetChildren(Resources.GetUserStoryChain(ToTest, ResourceManager.UserStory1Id), ToTest.TaskType).Count);
            Assert.AreEqual(
                ResourceManager.Project1UserStory2TaskCount,
                ToTest.WorkItems.GetChildren(Resources.GetUserStoryChain(ToTest, ResourceManager.UserStory2Id), ToTest.TaskType).Count);
            Assert.AreEqual(
                ResourceManager.Project1UserStoryCount,
                ToTest.WorkItems.GetChildren(new ValueChain<string>(ToTest.UniqueId), ToTest.UserStoryType).Count);
            Assert.AreEqual(
                ResourceManager.Project1UserStory2BugCount,
                ToTest.WorkItems.GetChildren(Resources.GetUserStoryChain(ToTest, ResourceManager.UserStory2Id), ToTest.BugType).Count);
            Assert.AreEqual(
                ResourceManager.Project1UserStory3BugCount,
                ToTest.WorkItems.GetChildren(Resources.GetUserStoryChain(ToTest, ResourceManager.Iteration1Id, ResourceManager.Iteration3Id), ToTest.BugType).Count);
        }

        [TestMethod]
        public void TestGetChangesIterationValues()
        {
            DateTime iterStart = DateTime.Now;
            DateTime iterEnd = iterStart.AddDays(5);

            CacheIteration iteration1 = ToTest.Iterations.TryGetById(ResourceManager.Iteration1Id);
            iteration1.StartDate = iterStart;
            iteration1.EndDate = iterEnd;

            // check change set
            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.AreEqual(1, changes.ChangedIterations.Count);
            Assert.AreEqual(ResourceManager.Project1IdentityCount, changes.Identities.Count);
            Assert.AreEqual(0, changes.ChangedWorkItems.Count);
            Assert.IsNotNull(changes.Project.Name);
            Assert.IsFalse(changes.ChangedIterations.First().Value.Element.IsContextConnected);

            var itBucket = changes.ChangedIterations.First();
            CacheIteration cacheIterClone = itBucket.Value.Element;

            Assert.AreEqual(iteration1.UniqueId, cacheIterClone.UniqueId);
            Assert.IsFalse(cacheIterClone.IsContextConnected);
            Assert.AreEqual(iterStart, cacheIterClone.StartDate);
            Assert.AreEqual(iterEnd, cacheIterClone.EndDate);
            Assert.AreNotEqual(CacheSavePairState.Saved, changes.IterationsToSync[itBucket.Key].State);

            // check cache after change set extraction
            Assert.IsFalse(iteration1.IsDirty);
            Assert.IsTrue(iteration1.IsContextConnected);
        }

        [TestMethod]
        public void TestGetChangesUserStroyValues()
        {
            string memberId = Resources.Member1.DisplayName;
            const double ranking = 2.5;
            const double storyPoints = 5.0;
            const string name = "Test Name";
            const int iterationId = ResourceManager.Iteration1Id;
            const string state = "Closed";
            const string description = "Description";

            CacheWorkItem userStory = ToTest.WorkItems.TryGetById(ResourceManager.UserStory1Id);
            userStory.Name = name;
            userStory.Iteration = Resources.Iteration1Proj1;
            userStory.State = state;
            userStory.Description = description;
            userStory[CacheContext.Schema.UserStorySchema.MemberId.SchemaMappedName] = memberId;
            userStory[CacheContext.Schema.UserStorySchema.Ranking.SchemaMappedName] = ranking;
            userStory[CacheContext.Schema.UserStorySchema.StoryPoints.SchemaMappedName] = storyPoints;

            // check change set
            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.AreEqual(1, changes.ChangedWorkItems.Count);
            Assert.AreEqual(0, changes.ChangedIterations.Count);

            // emulate data driver and create writeable identity store
            CacheIdentityStore store = changes.Identities.ToWriteable();
            Assert.AreEqual(ResourceManager.Project1IdentityCount, store.Count);

            Assert.IsNotNull(changes.Project.Name);
            Assert.IsFalse(changes.ChangedWorkItems.First().Value.Element.IsContextConnected);

            var savePair = changes.ChangedWorkItems.First();
            CacheWorkItem cacheWorkItemClone = savePair.Value.Element;

            Assert.AreEqual(userStory.UniqueId, cacheWorkItemClone.UniqueId);
            Assert.IsFalse(cacheWorkItemClone.IsContextConnected);
            Assert.AreEqual(name, userStory.Name);
            Assert.AreEqual(iterationId, userStory.IterationId);
            Assert.AreEqual(state, userStory.State);
            Assert.AreEqual(description, userStory.Description);
            Assert.AreEqual(memberId, userStory[CacheContext.Schema.UserStorySchema.MemberId.SchemaMappedName]);
            Assert.AreEqual(ranking, userStory[CacheContext.Schema.UserStorySchema.Ranking.SchemaMappedName]);
            Assert.AreEqual(storyPoints, userStory[CacheContext.Schema.UserStorySchema.StoryPoints.SchemaMappedName]);

            // check cache after change set extraction
            Assert.IsFalse(userStory.IsDirty);
            Assert.IsTrue(userStory.IsContextConnected);
        }

        [TestMethod]
        public void TestGetChangesTaskValues()
        {
            const string activity = "Testing";
            const double completedWork = 2.0;
            const double estimate = 2.0;
            const int priority = 4;
            const double stackRank = 9.0;
            const double remainingWork = 1.5;
            const string name = "Test Name";
            const int iterationId = ResourceManager.Iteration1Id;
            const string state = "Closed";
            const string description = "Description";

            CacheWorkItem task = ToTest.WorkItems.TryGetById(ResourceManager.Task1Id);
            task.Name = name;
            task.Iteration = Resources.Iteration1Proj1;
            task.State = state;
            task.Description = description;
            task[CacheContext.Schema.TaskSchema[TestFieldDefinitionSet.ActivityFieldName].SchemaMappedName] = activity;
            task[CacheContext.Schema.TaskSchema.CompletedWork.SchemaMappedName] = completedWork;
            task[CacheContext.Schema.TaskSchema.Estimate.SchemaMappedName] = estimate;
            task[CacheContext.Schema.TaskSchema[TestFieldDefinitionSet.PriorityFieldName].SchemaMappedName] = priority;
            task[CacheContext.Schema.TaskSchema.Ranking.SchemaMappedName] = stackRank;
            task[CacheContext.Schema.TaskSchema.RemainingWork.SchemaMappedName] = remainingWork;

            // check change set
            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.AreEqual(1, changes.ChangedWorkItems.Count);
            Assert.AreEqual(0, changes.ChangedIterations.Count);
            Assert.IsNotNull(changes.Project.Name);
            Assert.IsFalse(changes.ChangedWorkItems.First().Value.Element.IsContextConnected);

            var wiBucket = changes.ChangedWorkItems.First();
            CacheWorkItem cacheWorkItemClone = wiBucket.Value.Element;
            
            Assert.AreEqual(task.UniqueId, cacheWorkItemClone.UniqueId);
            Assert.IsFalse(cacheWorkItemClone.IsContextConnected);
            Assert.AreEqual(name, task.Name);
            Assert.AreEqual(iterationId, task.IterationId);
            Assert.AreEqual(state, task.State);
            Assert.AreEqual(description, task.Description);
            Assert.AreEqual(activity, task[CacheContext.Schema.TaskSchema[TestFieldDefinitionSet.ActivityFieldName].SchemaMappedName]);
            Assert.AreEqual(completedWork, task[CacheContext.Schema.TaskSchema.CompletedWork.SchemaMappedName]);
            Assert.AreEqual(estimate, task[CacheContext.Schema.TaskSchema.Estimate.SchemaMappedName]);
            Assert.AreEqual(priority, task[CacheContext.Schema.TaskSchema[TestFieldDefinitionSet.PriorityFieldName].SchemaMappedName]);
            Assert.AreEqual(stackRank, task[CacheContext.Schema.TaskSchema.Ranking.SchemaMappedName]);
            Assert.AreEqual(remainingWork, task[CacheContext.Schema.TaskSchema.RemainingWork.SchemaMappedName]);
            Assert.AreNotEqual(CacheSavePairState.Saved, changes.WorkItemsToSync[wiBucket.Key.Top].State);

            // check cache after change set extraction
            Assert.IsFalse(task.IsDirty);
            Assert.IsTrue(task.IsContextConnected);
        }


        [TestMethod]
        public void TestAcceptChangesWithReject()
        {
            CacheWorkItem task = ToTest.WorkItems.TryGetById(ResourceManager.Task1Id);
            task.Name = "Test Name";

            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.IsFalse(changes.Accepted);
            Assert.IsFalse(task.IsDirty);

            changes.WorkItemsToSync.First().Value.State = CacheSavePairState.Error;

            Assert.IsFalse(changes.Accepted);

            // this will reject the changes, so the changes could
            // not be saved to the backend system
            DataContext.ImportChanges(changes);  // do not Reset dirty flags, we emulate the error state
            Assert.IsTrue(task.IsDirty);
        }

        [TestMethod]
        public void TestAcceptSettingsChangesWithPartialReject()
        {
            CacheWorkItem bug = ToTest.WorkItems.TryGetById(ResourceManager.Bug1Id);
            bug.Name = "[New Name]";
            bug.State = bug.Type.StateField.AllowedValues.Second().ToString();
            bug.State = bug.Type.StateField.AllowedValues.Third().ToString();
            Assert.IsTrue(bug.IsDirty);

            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.IsFalse(changes.Accepted);
            Assert.IsFalse(bug.IsDirty);
            Assert.AreEqual(bug.Type.StateField.AllowedValues.Third(), bug.State);

            // simluate data driver, this means that the new work item data is given by the underlying server
            CacheElementListEntryBucket<CacheWorkItem> changedBug = changes.ChangedWorkItems.First().Value;

            // this must cause the reject procedure
            CacheSavePair<CacheElementListEntryBucket<CacheWorkItem>> newSavePair = new CacheSavePair<CacheElementListEntryBucket<CacheWorkItem>>(changedBug);
            Assert.AreEqual(CacheSavePairState.Error, newSavePair.State);
            
            // insort bucket into sync container
            changes.WorkItemsToSync[newSavePair.Bucket.Element.UniqueId] = newSavePair;
            Assert.IsFalse(changes.Accepted);

            // this will reject the changes, so the changes could
            // not be saved to the backend system
            DataContext.ImportChanges(changes);  // do not Reset dirty flags, we emulate the error state
            Assert.IsTrue(bug.IsDirty);
            Assert.AreEqual(bug.Type.StateField.AllowedValues.Third(), bug.State);
        }


        [TestMethod]
        public void TestAcceptChangesWithNewElement()
        {
            CacheWorkItem task = ToTest.WorkItems.TryGetById(ResourceManager.Task1Id);
            task.Name = "Test Name";

            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.IsFalse(changes.Accepted);
            Assert.IsFalse(task.IsDirty);

            // simluate data driver, this means that the new work item data is given by the underlying server
            CacheElementListEntryBucket<CacheWorkItem> bucket = changes.ChangedWorkItems.First().Value;
            CacheElementFactory idElementFactory = new CacheIdentityElementFactory(
                changes.Identities,
                changes.IdentitiesIdGenerator);

            // server might create a new task in order to provide the server-side updates
            CacheWorkItem newElemement = idElementFactory.CreateWorkItem(
                bucket.Element.Type, bucket.Element.Id, bucket.Element.Name,
                bucket.Element.Revision + 1, bucket.Element.IterationId, bucket.Element.State,
                bucket.Element.Description, bucket.Element.AssignedTo, bucket.Element.ToValuesDictionary(),
                bucket.Element.ParentLinks, bucket.Element.ChildLinks, bucket.Element.Attachments,
                bucket.Element.Revisions);

            newElemement.ImportSettings(bucket.Element.ToSettingsDictionary());

            // make sure the new clone instance has the same id (provided by the factory)
            Assert.AreEqual(bucket.Element.UniqueId, newElemement.UniqueId);

            bucket = new CacheElementListEntryBucket<CacheWorkItem> { Element = newElemement };
            changes.WorkItemsToSync[newElemement.UniqueId] = new CacheSavePair<CacheElementListEntryBucket<CacheWorkItem>>(bucket)
                                                                {
                                                                    State = CacheSavePairState.Saved
                                                                };
            changes.RemoveDirtyFlags();

            // this will accept the changes, so the changes are saved to the backend system
            DataContext.ImportChanges(changes);
            Assert.IsTrue(changes.Accepted);
            Assert.IsFalse(task.IsDirty);
        }

        [TestMethod]
        public void TestRejectChangesWithNewElement()
        {
            CacheWorkItem task = ToTest.WorkItems.TryGetById(ResourceManager.Task1Id);
            task.Name = "Test Name";

            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.IsFalse(changes.Accepted);
            Assert.IsFalse(task.IsDirty);

            // simluate data driver, this means that the new work item data is given by the underlying server
            CacheElementListEntryBucket<CacheWorkItem> bucket = changes.ChangedWorkItems.First().Value;
            CacheElementFactory idElementFactory = new CacheIdentityElementFactory(
                changes.Identities.ToWriteable(),
                changes.IdentitiesIdGenerator);

            // server might create a new task in order to provide the server-side updates
            CacheWorkItem newElemement = idElementFactory.CreateWorkItem(
                bucket.Element.Type, bucket.Element.Id, bucket.Element.Name,
                bucket.Element.Revision + 1, bucket.Element.IterationId, bucket.Element.State,
                bucket.Element.Description, bucket.Element.AssignedTo, bucket.Element.ToValuesDictionary(),
                bucket.Element.ParentLinks, bucket.Element.ChildLinks, bucket.Element.Attachments,
                bucket.Element.Revisions);

            newElemement.ImportSettings(bucket.Element.ToSettingsDictionary());

            // make sure the new clone instance has the same id (provided by the factory)
            Assert.AreEqual(bucket.Element.UniqueId, newElemement.UniqueId);

            bucket = new CacheElementListEntryBucket<CacheWorkItem> { Element = newElemement };
            changes.WorkItemsToSync[newElemement.UniqueId] = new CacheSavePair<CacheElementListEntryBucket<CacheWorkItem>>(bucket)
            {
                State = CacheSavePairState.Error
            };
            changes.RemoveDirtyFlags();

            // this will import the changes, but the error element could
            // not be synced from the backend system into the local storage
            // because it changed by the data driver
            DataContext.ImportChanges(changes);

            // the changes aren't accepted
            Assert.IsFalse(changes.Accepted);
            Assert.IsFalse(task.IsDirty);
        }

        [TestMethod]
        public void TestAcceptChangesWithAccept()
        {
            CacheWorkItem task = ToTest.WorkItems.TryGetById(ResourceManager.Task1Id);
            task.Name = "Test Name";

            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.IsFalse(changes.Accepted);
            Assert.IsFalse(task.IsDirty);

            // do accept
            changes.WorkItemsToSync.First().Value.State = CacheSavePairState.Saved;
            Assert.AreEqual(CacheSavePairState.Saved, changes.WorkItemsToSync.First().Value.State);
            changes.RemoveDirtyFlags();

            // this will accept the changes, so the changes are saved to the backend system
            DataContext.ImportChanges(changes);
            Assert.IsTrue(changes.Accepted);
            Assert.IsFalse(task.IsDirty);
        }

        [TestMethod]
        public void TestAcceptChangesWithIdRetrieve()
        {
            int newTaskId = 100;
            CacheWorkItem task = ToTest.WorkItems.TryGetById(ResourceManager.Task1Id);
            task.Name = "Test Name";

            CacheDataChanges changes = DataContext.ExportChanges();
            Assert.IsFalse(changes.Accepted);
            Assert.IsFalse(task.IsDirty);

            // do accept
            changes.WorkItemsToSync.First().Value.State = CacheSavePairState.Saved;
            changes.WorkItemsToSync.First().Value.Bucket.Element.AssignId(newTaskId);
            changes.RemoveDirtyFlags();

            DataContext.ImportChanges(changes);
            Assert.IsTrue(changes.Accepted);
            Assert.IsFalse(task.IsDirty);
            Assert.AreEqual(task.Id, newTaskId);
        }
        
        [TestMethod]
        public void TestAcceptChangesWithRelationIdRetrieve()
        {
            int newParentId = 100;
            int newChildId = 110;
            CacheWorkItem parentStory = ToTest.WorkItems.TryGetById(ResourceManager.UserStory1Id);
            CacheWorkItem childTask = ToTest.WorkItems.TryGetById(ResourceManager.Task1Id);
            CacheWorkItemLink newParentLink = ToTest.OwnerContext.ElementFactory.CreateWorkItemLink(parentStory.Id, parentStory.UniqueId);
            CacheWorkItemLink newChildLink = ToTest.OwnerContext.ElementFactory.CreateWorkItemLink(childTask.Id, childTask.UniqueId);

            childTask.ParentLinks.Add(newParentLink);

            CacheDataChanges changes = DataContext.ExportChanges();

            // accept buckets and fill new id
            foreach (var changeBucket in changes.ChangedWorkItems)
            {
                // do accept
                changes.WorkItemsToSync[changeBucket.Key.Top].State = CacheSavePairState.Saved;

                if (changeBucket.Value.Element.UniqueId == parentStory.UniqueId)
                    changeBucket.Value.Element.AssignId(newParentId);
                else if (changeBucket.Value.Element.UniqueId == childTask.UniqueId)
                    changeBucket.Value.Element.AssignId(newChildId);
            }

            changes.RemoveDirtyFlags();
            DataContext.ImportChanges(changes);

            // the new ids must be filled into the relation links
            Assert.AreEqual(newParentId, parentStory.Id);
            Assert.AreEqual(newChildId, childTask.Id);
            Assert.IsTrue(childTask.ParentLinks.Contains(newParentLink));
            Assert.IsTrue(parentStory.ChildLinks.Contains(newChildLink));
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}
